#include <crt_sockinet.h>

int	_valid_hostaddr(const char *addr, int gripe)
{
	if (*addr == 0) {
		return 0;
	}
	if (strchr(addr, ':') != 0)
		return _valid_ipv6_hostaddr(addr, gripe);
	else
		return _valid_ipv4_hostaddr(addr, gripe);
}

int	_valid_ipv4_hostaddr(const char *addr_in, int gripe)
{
	char    addr[128] = { 0x00 };
	char	*ptr = NULL;
	const char *cp;
	int     in_byte = 0;
	int     byte_count = 0;
	int     byte_val = 0;
	int     ch;

#define BYTES_NEEDED	4

	strcpy(addr, addr_in);

	if ((ptr = strrchr(addr, '|')) != NULL) {
		*ptr = 0;
	}
	else if ((ptr = strrchr(addr, ':')) != NULL) {
		*ptr = 0;
	}

	/*
	* Scary code to avoid sscanf() overflow nasties.
	*
	* This routine is called by valid_ipv6_hostaddr(). It must not call that
	* routine, to avoid deadly recursion.
	*/
	for (cp = addr; (ch = *(unsigned const char *)cp) != 0; cp++) {


		int id = isdigit(ch);

		if (id) {
			if (in_byte == 0) {
				in_byte = 1;
				byte_val = 0;
				byte_count++;
			}
			byte_val *= 10;
			byte_val += ch - '0';
			if (byte_val > 255) {

				return 0;
			}
		}
		else if (ch == '.') {
			if (in_byte == 0 || cp[1] == 0) {

				return 0;
			}
			/* XXX Allow 0.0.0.0 but not 0.1.2.3 */
			if (byte_count == 1 && byte_val == 0
				&& addr[strspn(addr, "0.")]) {

				return 0;
			}
			in_byte = 0;
		}
		else {

			return 0;
		}
	}

	if (byte_count != BYTES_NEEDED && strcmp(addr, "0") != 0) {

		return 0;
	}

	return 1;
}

int	_valid_ipv6_hostaddr(const char *addr_in, int gripe)
{
	int   null_field = 0;
	int   field = 0;
	char  addr[128] = { 0x00 };
	char	*ptr = NULL;

	const unsigned char *cp = (const unsigned char *)addr;
	int   len = 0;

	strcpy(addr, addr_in);
	if ((ptr = strrchr(addr, '|')) != NULL) {
		*ptr = 0;
	}

	if ((ptr = strrchr(addr, '%')) != NULL) {
		*ptr = 0;
	}

	cp = (const unsigned char *)addr;

	/*
	* FIX 200501 The IPv6 patch validated syntax with getaddrinfo(), but I
	* am not confident that everyone's system library routines are robust
	* enough, like buffer overflow free. Remember, the valid_hostmumble()
	* routines are meant to protect Postfix against malformed information
	* in data received from the network.
	*
	* We require eight-field hex addresses of the form 0:1:2:3:4:5:6:7,
	* 0:1:2:3:4:5:6a.6b.7c.7d, or some :: compressed version of the same.
	*
	* Note: the character position is advanced inside the loop. I have added
	* comments to show why we can't get stuck.
	*/
	for (;;) {
		switch (*cp) {
		case 0:
			/* Terminate the loop. */
			if (field < 2) {

				return 0;
			}
			else if (len == 0 && null_field != field - 1) {

				return 0;
			}
			else
				return 1;
		case '.':
			/* Terminate the loop. */
			if (field < 2 || field > 6) {

				return 0;
			}
			/* NOT: valid_hostaddr(). Avoid recursion. */
			return _valid_ipv4_hostaddr((const char *)cp - len, gripe);
		case ':':
			/* advance by exactly 1 character position or terminate. */
			if (field == 0 && len == 0 && isalnum((int)cp[1])) {

				return 0;
			}
			field++;
			if (field > 7) {

				return 0;
			}
			cp++;
			len = 0;
			if (*cp == ':') {
				if (null_field > 0) {

					return 0;
				}
				null_field = field;
			}
			break;
		default:
			/* Advance by at least 1 character position or terminate. */
			len = (int)strspn((const char *)cp, "0123456789abcdefABCDEF");
			if (len /* - strspn((char *) cp, "0") */ > 4) {

				return 0;
			}
			if (len <= 0) {

				return 0;
			}
			cp += len;
			break;
		}
	}
}

int	_is_ipv4(const char *ip)
{       
	return _valid_ipv4_hostaddr(ip, 0);
}

int	_is_ipv6(const char *ip)
{
	return _valid_ipv6_hostaddr(ip, 0);
}

int	_is_ip(const char *ip)
{
	return _is_ipv4(ip) || _is_ipv6(ip);
}

int	_ipv4_addr_valid(const char *addr)
{
	const char *ptr = addr;
	int   n, k;

	if (addr == NULL || *addr == 0)
		return (0);
	k = 3;
	while (*ptr && *ptr != '.') {
		n = *ptr;
		if (n < '0' || n > '9')
			return (0);
		ptr++;
		k--;
		if (k < 0)
			return (0);
	}
	if (*ptr == 0)
		return (0);
	
	k = 3;
	ptr++;
	while (*ptr && *ptr != '.') {
		n = *ptr;
		if (n < '0' || n > '9')
			return (0);
		ptr++;
		k--;
		if (k < 0)
			return (0);
	}
	if (*ptr == 0)
		return (0);
	
	k = 3;
	ptr++;
	while (*ptr && *ptr != '.') {
		n = *ptr;
		if (n < '0' || n > '9')
			return (0);
		ptr++;
		k--;
		if (k < 0)
			return (0);
	}
	if (*ptr == 0)
		return (0);
	
	k = 3;
	ptr++;
	while (*ptr && *ptr != ':') {
		n = *ptr;
		if (n < '0' || n > '9')
			return (0);
		ptr++;
		k--;
		if (k < 0)
			return (0);
	}
	if (*ptr == 0)
		return (0);

	ptr++;
	n = atoi(ptr);
	if (n < 0 || n > 65535)
		return (0);
	return (1);
}

#define IN6ADDRSZ       16
#define INADDRSZ         4
#define INT16SZ          2
int inet_pton4(const char *src, unsigned char *dst)
{
	const char digits[] = "0123456789";
	int saw_digit, octets, ch;
	unsigned char tmp[INADDRSZ], *tp;

	saw_digit = 0;
	octets = 0;
	tp = tmp;
	*tp = 0;
	while ((ch = *src++) != '\0') {
		const char *pch;

		pch = strchr(digits, ch);
		if (pch) {
			unsigned int val = *tp * 10 + (unsigned int)(pch - digits);

			if (saw_digit && *tp == 0)
				return (0);
			if (val > 255)
				return (0);
			*tp = (unsigned char)val;
			if (!saw_digit) {
				if (++octets > 4)
					return (0);
				saw_digit = 1;
			}
		}
		else if (ch == '.' && saw_digit) {
			if (octets == 4)
				return (0);
			*++tp = 0;
			saw_digit = 0;
		}
		else
			return (0);
	}
	if (octets < 4)
		return (0);
	memcpy(dst, tmp, INADDRSZ);
	return (1);
}

int inet_pton6(const char *src, unsigned char *dst)
{
	const char xdigits_l[] = "0123456789abcdef",
		xdigits_u[] = "0123456789ABCDEF";
	unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
	const char *xdigits, *curtok;
	int ch, saw_xdigit;
	size_t val;

	memset((tp = tmp), 0, IN6ADDRSZ);
	endp = tp + IN6ADDRSZ;
	colonp = NULL;
	/* Leading :: requires some special handling. */
	if (*src == ':')
		if (*++src != ':')
			return (0);
	curtok = src;
	saw_xdigit = 0;
	val = 0;
	while ((ch = *src++) != '\0') {
		const char *pch;

		pch = strchr((xdigits = xdigits_l), ch);
		if (!pch)
			pch = strchr((xdigits = xdigits_u), ch);
		if (pch != NULL) {
			val <<= 4;
			val |= (pch - xdigits);
			if (++saw_xdigit > 4)
				return (0);
			continue;
		}
		if (ch == ':') {
			curtok = src;
			if (!saw_xdigit) {
				if (colonp)
					return (0);
				colonp = tp;
				continue;
			}
			if (tp + INT16SZ > endp)
				return (0);
			*tp++ = (unsigned char)((val >> 8) & 0xff);
			*tp++ = (unsigned char)(val & 0xff);
			saw_xdigit = 0;
			val = 0;
			continue;
		}
		if (ch == '.' && ((tp + INADDRSZ) <= endp) &&
			inet_pton4(curtok, tp) > 0) {
			tp += INADDRSZ;
			saw_xdigit = 0;
			break;    /* '\0' was seen by inet_pton4(). */
		}
		return (0);
	}
	if (saw_xdigit) {
		if (tp + INT16SZ > endp)
			return (0);
		*tp++ = (unsigned char)((val >> 8) & 0xff);
		*tp++ = (unsigned char)(val & 0xff);
	}
	if (colonp != NULL) {
		/*
		* Since some memmove()'s erroneously fail to handle
		* overlapping regions, we'll do the shift by hand.
		*/
		const ssize_t n = tp - colonp;
		ssize_t i;

		if (tp == endp)
			return (0);
		for (i = 1; i <= n; i++) {
			*(endp - i) = *(colonp + n - i);
			*(colonp + n - i) = 0;
		}
		tp = endp;
	}
	if (tp != endp)
		return (0);
	memcpy(dst, tmp, IN6ADDRSZ);
	return (1);
}

int	_inet_pton(int af, const char *src, void *dst) 
{
	switch (af) {
	case AF_INET:
		return (inet_pton4(src, (unsigned char *)dst));
	case AF_INET6:
		return (inet_pton6(src, (unsigned char *)dst));
	default:
		break;
	}
	return -1;
}


char* inet_ntop4(const unsigned char *src, char *dst, size_t size)
{
	char tmp[sizeof("255.255.255.255")];
	size_t len;

	tmp[0] = '\0';
	sprintf(tmp, "%d.%d.%d.%d",
		((int)((unsigned char)src[0])) & 0xff,
		((int)((unsigned char)src[1])) & 0xff,
		((int)((unsigned char)src[2])) & 0xff,
		((int)((unsigned char)src[3])) & 0xff);

	//a[0] = ((inaddr & 0xff000000) >> 24);
	//a[1] = ((inaddr & 0x00ff0000) >> 16);
	//a[2] = ((inaddr & 0x0000ff00) >> 8);
	//a[3] = ((inaddr & 0x000000ff) >> 0);

	len = strlen(tmp);
	if (len == 0 || len >= size) {
		errno = ENOSPC;
		return (NULL);
	}
	strcpy(dst, tmp);
	return dst;
}

char* inet_ntop6(const unsigned char *src, char *dst, size_t size)
{
	char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
	char *tp;
	struct {
		long base;
		long len;
	} best, cur;
	unsigned long words[IN6ADDRSZ / INT16SZ];
	int i;

	/* Preprocess:
	*  Copy the input (bytewise) array into a wordwise array.
	*  Find the longest run of 0x00's in src[] for :: shorthanding.
	*/
	memset(words, '\0', sizeof(words));
	for (i = 0; i < IN6ADDRSZ; i++)
		words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));

	best.base = -1;
	cur.base = -1;
	best.len = 0;
	cur.len = 0;

	for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
		if (words[i] == 0) {
			if (cur.base == -1)
                (void)(cur.base = i), cur.len = 1;
			else
				cur.len++;
		}
		else if (cur.base != -1) {
			if (best.base == -1 || cur.len > best.len)
				best = cur;
			cur.base = -1;
		}
	}
	if ((cur.base != -1) && (best.base == -1 || cur.len > best.len))
		best = cur;
	if (best.base != -1 && best.len < 2)
		best.base = -1;
	/* Format the result. */
	tp = tmp;
	for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
		/* Are we inside the best run of 0x00's? */
		if (best.base != -1 && i >= best.base && i < (best.base + best.len)) {
			if (i == best.base)
				*tp++ = ':';
			continue;
		}

		/* Are we following an initial run of 0x00s or any real hex?
		*/
		if (i != 0)
			*tp++ = ':';

		/* Is this address an encapsulated IPv4?
		*/
		if (i == 6 && best.base == 0 &&
			(best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
			if (!inet_ntop4(src + 12, tp, sizeof(tmp) - (tp - tmp))) {
				errno = ENOSPC;
				return (NULL);
			}
			tp += strlen(tp);
			break;
		}
		tp += sprintf(tp,"%lx", words[i]);
	}

	/* Was it a trailing run of 0x00's?
	*/
	if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ))
		*tp++ = ':';
	*tp++ = '\0';

	/* Check for overflow, copy, and we're done.
	*/
	if ((size_t)(tp - tmp) > size) {
		errno = ENOSPC;
		return (NULL);
	}
	strcpy(dst, tmp);
	return dst;
}

char* _inet_ntop(int af, const void *src, char *buf, size_t size)
{
	switch (af) {
	case AF_INET:
		return inet_ntop4((const unsigned char *)src, buf, size);
	case AF_INET6:
		return inet_ntop6((const unsigned char *)src, buf, size);
	default:
		break;
	}
	return NULL;
}
unsigned int _inet_addr_v4(const char* ip)
{
	unsigned int a = 0;
	unsigned int b = 0;
	unsigned int c = 0;
	unsigned int d = 0;
	unsigned int address = 0;
	sscanf(ip, "%u.%u.%u.%u", &a, &b, &c, &d);
	
	address |= d << 24;
	address |= c << 16;
	address |= b << 8;
	address |= a;
	return address;
}

